package models;

public class Logic
{
    private static Logic thisLogic = null;
    private Board board;
    private int[][] solution = null;
    private int[][] initialMatrix = null;


    private Logic()
    {
        board = Board.getInstance();
    }

    public static synchronized Logic getInstance()
    {
        if (thisLogic == null)
            thisLogic = new Logic();

        return thisLogic;
    }

    public boolean verify()
    {
        board = Board.getInstance();
        boolean verifies = true;
        int index = 0;

        while (verifies && (index < 9))
        {
            verifies = checkRow(index) && checkColumn(index);
            index++;
        }

        return verifies && checkBlocks();
    }

    private boolean checkRow(int i)
    {
        boolean[] existingNumbers = new boolean[9]; //initialize false
        int actual;
        int column = 0;
        boolean checks = true;

        while ((checks) && (column < 9))
        {
            actual = board.getNumber(i, column);
            if (checks = (actual != 0) && !existingNumbers[actual - 1])
                existingNumbers[actual - 1] = true;

            column++;
        }

        return checks;
    }

    private boolean checkColumn(int j)
    {
        boolean[] existingNumbers = new boolean[9]; //initialize false
        int actual;
        int row = 0;
        boolean checks = true;

        while ((checks) && (row < 9))
        {
            actual = board.getNumber(row, j);
            if (checks = (actual != 0) && !existingNumbers[actual - 1]);
            existingNumbers[actual - 1] = true;

            row++;
        }

        return checks;
    }

    private boolean checkBlock(int i, int j)
    {
        boolean[] existingNumbers = new boolean[9]; //initialize false
        int actual;
        boolean checks = true;

        int auxi = i;
        while (auxi < i && checks)
        {
            int auxj = j;

            while (auxj < j + 3 && checks)
            {
                actual = board.getNumber(auxi, auxj);
                if (checks = (actual != 0) && !existingNumbers[actual - 1])
                    existingNumbers[actual - 1] = true;

                auxj++;
            }

            auxi++;
        }

        return checks;
    }

    private boolean checkBlocks()
    {
        return checkBlock(6, 0) && checkBlock(0, 0) && checkBlock(3, 0)  &&
               checkBlock(0, 3) && checkBlock(3, 3) && checkBlock(6, 3)  &&
               checkBlock(0, 6) && checkBlock(3, 6) && checkBlock(6, 6);
    }

    //TODO: pedir enumerado con dificultad y segun eso
    //setear initialMatrix y solutionMatrix y board.
    public void generate(int configuracion)
    {
        int rnd1 = getRandom();
        int rnd2 = getRandom();

        int [][] initial;
        initial = confg1();
        switch (configuracion)
        {
            case 1:
                //por default
                break;

            case 2:
                initial = confg2();
                break;

            case 3:
                initial = confg3();
                break;
        }


        int [][] sol =
        {
            {5, 3, 1, 7, 2, 9, 6, 8, 4},
            {6, 2, 8, 1, 4, 3, 9, 5, 7},
            {9, 7, 4, 5, 6, 8, 1, 3, 2},
            {8, 4, 9, 6, 5, 7, 3, 2, 1},
            {2, 5, 3, 4, 9, 1, 8, 7, 6},
            {7, 1, 6, 3, 8, 2, 5, 4, 9},
            {4, 8, 7, 9, 3, 6, 2, 1, 5},
            {3, 6, 5, 2, 1, 4, 7, 9, 8},
            {1, 9, 2, 8, 7, 5, 4, 6, 3},
        };

        for (int i = 0; i < 9; i++)
            for (int j = 0; j < 9; j++)
            {
                if ((sol[i][j] == rnd1))
                {
                    initial[i][j] = rnd2;
                    sol[i][j] = rnd2;
                }
                else if (sol [i][j] == rnd2)
                {
                    initial[i][j] = rnd1;
                    sol[i][j] = rnd1;
                }
            }

        setInitialMatrix(initial);
        setSolutionMatrix(sol);
        board.setMatrix(initial);
    }

    private int [][] confg1()
    {
        int [][] matriz =
        {
            {5, 0, 1, 0, 2, 9, 0, 8, 4},
            {0, 2, 8, 1, 4, 3, 0, 5, 7},
            {9, 7, 4, 0, 6, 8, 1, 0, 0},
            {8, 4, 9, 0, 5, 7, 3, 2, 1},
            {0, 5, 3, 4, 0, 1, 8, 7, 0},
            {7, 1, 6, 0, 8, 2, 0, 4, 9},
            {0, 8, 7, 9, 3, 6, 0, 1, 5},
            {3, 6, 5, 2, 0, 4, 7, 9, 8},
            {1, 0, 2, 0, 7, 5, 4, 0, 3},
        };
        return matriz;
    }

    private int [][] confg2()
    {
        int [][] matriz =
        {
            {5, 0, 1, 0, 2, 0, 0, 8, 0},
            {0, 2, 8, 1, 0, 3, 0, 0, 7},
            {9, 0, 4, 0, 6, 0, 1, 0, 0},
            {8, 0, 9, 0, 5, 7, 0, 2, 1},
            {0, 5, 0, 4, 0, 1, 8, 0, 0},
            {7, 0, 6, 0, 8, 2, 0, 4, 9},
            {0, 8, 0, 9, 3, 6, 0, 1, 0},
            {3, 6, 0, 2, 0, 0, 7, 0, 8},
            {1, 0, 2, 0, 7, 0, 4, 0, 3},
        };
        return matriz;
    }

    private int [][] confg3()
    {
        int [][] matriz =
        {
            {5, 0, 1, 0, 0, 0, 0, 0, 0},
            {0, 2, 0, 0, 0, 0, 0, 5, 0},
            {0, 0, 0, 0, 6, 8, 1, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 1},
            {0, 0, 0, 4, 0, 0, 8, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 8, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 4, 0, 9, 0},
            {0, 0, 2, 0, 7, 0, 4, 0, 0},
        };
        return matriz;
    }

    private int getRandom()
    {
        int result;

        do
            result = (int)(Math.random() * 10);
        while (result == 0);

        return result;
    }

    //TODO: que tome un enumerado "nivel" y devuelva una matriz con dicho nivel
    public int[][] getSolutionMatrix()
    {
        return this.solution;
    }

    private void setSolutionMatrix(int[][] solutionMatrix)
    {
        this.solution = solutionMatrix;
    }

    private void setInitialMatrix(int [][] initialMatrix)
    {
        this.initialMatrix = initialMatrix;
    }

    public int[][] getInitialMatrix()
    {
        return initialMatrix;
    }
}
